package top.system.dept.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.codec.Charsets;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;

import javax.net.ssl.SSLException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.*;

/**
 * @ClassName: NetWork
 * @Author: Amosen
 * @Since: 2021/5/19
 */

// 这个类专门用于发起网络请求并接受数据，便于和微信服务器对接
// 所返回的数据尽量不要为null，避免出现空指针异常，可以返回一个空对象
public class NetWork {

    private static Logger logger = LoggerFactory.getLogger(NetWork.class);

    // http请求客户端
    private static HttpClient httpClient;

    // http请求配置
    private static RequestConfig requestConfig;

    static { // 静态追加配置项
        // Socket配置
        SocketConfig socketConfig = SocketConfig.custom()
                .setSoTimeout(ConstantPool.CONNECTION_TIMEOUT)
                .setSoKeepAlive(true)
                .setTcpNoDelay(true)
                .build();

        // request配置
        requestConfig = RequestConfig.custom()
                .setSocketTimeout(ConstantPool.CONNECTION_TIMEOUT)
                .setConnectTimeout(ConstantPool.REQUEST_TIMEOUT)
                .build();

        // HttpClientPool配置
        PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
        manager.setDefaultSocketConfig(socketConfig);
        manager.setMaxTotal(ConstantPool.MAX_CONNECTION); // 设置最大连接数
        manager.setDefaultMaxPerRoute(ConstantPool.MAX_CONCURRENT_CONNECTION); // 设置最大并发连接数

        // 设置重试处理器
        httpClient = HttpClients.custom()
                .setConnectionManager(manager)
                .setRetryHandler(new CustomRetryHandler()).build();
    }

    public static Map<String, Object> service(String url,
                                              HttpMethod method,
                                              Map<String, String> data,
                                              Map<String, String> headers) {
        if (method == HttpMethod.GET) {
            return doGet(url, data, headers);
        } else if (method == HttpMethod.POST) {
            return doPost(url, data, headers);
        }
        return new HashMap<>();
    }

    public static Map<String, Object> doGet(String url, Map<String, String> datas, Map<String, String> headers) {
        try {
            // 构建uri
            URIBuilder uriBuilder = new URIBuilder(url);
            // 将请求参数添加到uri中
            if (datas != null) {
                for (Entry<String, String> data : datas.entrySet()) {
                    uriBuilder.addParameter(data.getKey(), data.getValue());
                }
            }
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            // 添加请求头
            if (headers != null) {
                for (Entry<String, String> header : headers.entrySet()) {
                    httpGet.addHeader(header.getKey(), header.getValue());
                }
            }

            // 添加配置项
            httpGet.setConfig(requestConfig);
            // 发起请求
            HttpResponse response = httpClient.execute(httpGet);
            // 获取请求数据并转为map对象返回
            ObjectMapper om = new ObjectMapper();
            Map data = om.readValue(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8), Map.class);
            return data;

        } catch (URISyntaxException e) {
            logger.error("Get服务被传递了一个错误的URL", e);
        } catch (ClientProtocolException e) {
            logger.error("Get服务协议异常", e);
        } catch (JsonMappingException e) {
            logger.error("Get服务未收到正常格式的JSON数据");
        } catch (JsonProcessingException e) {
            logger.error("Get服务未收到正常格式的JSON数据");
        } catch (IOException e) {
            logger.error("Get服务IO异常", e);
        }
        return new HashMap<>();
    }

    public static Map<String, Object> doPost(String url, Map<String, String> datas, Map<String, String> headers) {
        try {
            // 构建uri
            URIBuilder uriBuilder = new URIBuilder(url);
            // 将请求参数添加到uri中
            if (datas != null) {
                for (Entry<String, String> data : datas.entrySet()) {
                    uriBuilder.addParameter(data.getKey(), data.getValue());
                }
            }
            HttpPost httpPost = new HttpPost(uriBuilder.build());
            // 添加请求头
            if (headers != null) {
                for (Entry<String, String> header : headers.entrySet()) {
                    httpPost.addHeader(header.getKey(), header.getValue());
                }
            }

            // 添加配置项
            httpPost.setConfig(requestConfig);
            // 发起请求
            HttpResponse response = httpClient.execute(httpPost);
            // 获取请求数据并转为map对象返回
            ObjectMapper om = new ObjectMapper();
            Map data = om.readValue(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8), Map.class);
            return data;

        } catch (URISyntaxException e) {
            logger.error("Get服务被传递了一个错误的URL", e);
        } catch (ClientProtocolException e) {
            logger.error("Get服务协议异常", e);
        } catch (JsonMappingException e) {
            logger.error("Get服务未收到正常格式的JSON数据");
        } catch (JsonProcessingException e) {
            logger.error("Get服务未收到正常格式的JSON数据");
        } catch (IOException e) {
            logger.error("Get服务IO异常", e);
        }
        return new HashMap<>();
    }

    public static Map<String, Object> doPostWithJsonData(String url, Map<String, Object> datas, Map<String, String> headers) {
        if (url == null) {
            return new HashMap<>();
        }
        String requestDatas = "";
        if (datas != null) {
            ObjectMapper om = new ObjectMapper();
            try {
                requestDatas = om.writeValueAsString(datas);
            } catch (JsonProcessingException e) {
                return new HashMap<>();
            }
        }

        HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(new StringEntity(requestDatas, ContentType.create("application/json", "utf-8")));
        httpPost.setConfig(requestConfig);
        if (headers != null) {
            for(Entry<String, String> header : headers.entrySet()) {
                httpPost.setHeader(header.getKey(), header.getValue());
            }
        }
        try {
            HttpResponse response = httpClient.execute(httpPost);
            if (response != null) {
                ObjectMapper om = new ObjectMapper();
                Map data = om.readValue(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8), Map.class);
                return data;
            } else {
                return new HashMap<>();
            }
        } catch (IOException e) {
            logger.error("IO异常！", e);
            return new HashMap<>();
        }
    }

    public static Map<String, Object> doPostWithJsonStr(String url, String jsonStr, Map<String, String> headers) {
        if (url == null) {
            return new HashMap<>();
        }

        HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(new StringEntity(jsonStr, ContentType.create("application/json", "utf-8")));
        httpPost.setConfig(requestConfig);
        if (headers != null) {
            for(Entry<String, String> header : headers.entrySet()) {
                httpPost.setHeader(header.getKey(), header.getValue());
            }
        }
        try {
            HttpResponse response = httpClient.execute(httpPost);
            if (response != null) {
                ObjectMapper om = new ObjectMapper();
                Map data = om.readValue(EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8), Map.class);
                return data;
            } else {
                return new HashMap<>();
            }
        } catch (IOException e) {
            logger.error("IO异常！", e);
            return new HashMap<>();
        }
    }

}

// 重试处理器
class CustomRetryHandler implements HttpRequestRetryHandler {

    // 返回true会继续重试，返回false将不再重试
    @Override
    public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
        if (executionCount >= ConstantPool.MAX_FAIL_RETRY_COUNT) { // 超过重试次数
            return false;
        }
        if (exception instanceof InterruptedIOException) { // IO被打断
            return true;
        }
        if (exception instanceof UnknownHostException) { // 未知主机
            return false;
        }
        if (exception instanceof ConnectTimeoutException) { // 超时
            return true;
        }
        if (exception instanceof SSLException) { // SSL握手异常
            return false;
        }
        // 判断是否为幂等请求
        HttpClientContext clientContext = HttpClientContext.adapt(context);
        HttpRequest request = clientContext.getRequest();
        boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
        return idempotent;
    }
}
